package com.cskaoyan.mall.payment.service.impl;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cskaoyan.mall.common.constant.RedisConst;
import com.cskaoyan.mall.common.constant.ResultCodeEnum;
import com.cskaoyan.mall.common.execption.BusinessException;
import com.cskaoyan.mall.common.result.Result;
import com.cskaoyan.mall.order.constant.OrderStatus;
import com.cskaoyan.mall.order.dto.OrderInfoDTO;
import com.cskaoyan.mall.pay.api.dto.PaymentInfoDTO;
import com.cskaoyan.mall.payment.alipay.*;
import com.cskaoyan.mall.payment.client.OrderApiClient;
import com.cskaoyan.mall.payment.constant.PaymentStatus;
import com.cskaoyan.mall.payment.constant.PaymentType;
import com.cskaoyan.mall.payment.converter.PaymentInfoConverter;
import com.cskaoyan.mall.payment.mapper.PaymentInfoMapper;
import com.cskaoyan.mall.payment.model.PaymentInfo;
import com.cskaoyan.mall.payment.service.PayService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;

@Service
@Slf4j
public class PayServiceImpl implements PayService {
    @Autowired
    OrderApiClient orderApiClient;
    @Autowired
    PaymentInfoConverter paymentInfoConverter;
    @Autowired
    PaymentInfoMapper paymentInfoMapper;
    @Autowired
    CsmallAlipayConfig csmallAlipayConfig;
    @Autowired
    RedissonClient redissonClient;
    @Autowired
    PayHelperFactory payHelperFactory;

    PayHelper payHelper;

    @PostConstruct
    public void init(){
        payHelper= payHelperFactory.createPayHelper(PayTypeEnum.ALI.name());
    }
    @Override
    public String createAliPay(Long orderId) throws AlipayApiException {
        //创建支付的返回内容，步骤如下
        //通过orderId获取order表内容，构造一条payInfo表内容，然后插入payInfo内容，调用阿里云api返回对应页面内容
        OrderInfoDTO orderInfoDTO = orderApiClient.getOrderInfoDTO(orderId);
        if (orderInfoDTO==null||!OrderStatus.UNPAID.name().equals(orderInfoDTO.getOrderStatus())){
            throw new BusinessException(ResultCodeEnum.FAIL);
        }
        //判断一下，如果有对应的orderId就不插入了
        LambdaQueryWrapper<PaymentInfo> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(PaymentInfo::getOrderId,orderId);
        Long orderCount = paymentInfoMapper.selectCount(lambdaQueryWrapper);
        if (orderCount<1){
            //构造payInfo
            PaymentInfo paymentInfo = paymentInfoConverter.contvertOrderInfoDTO2PaymentInfo(orderInfoDTO);
            //信息补全，补全信息
            paymentInfo.setId(null);
            paymentInfo.setPaymentType(PaymentType.ALIPAY.name());
            paymentInfo.setTradeNo(null);
            paymentInfo.setPaymentStatus(PaymentStatus.UNPAID.name());

            int insert = paymentInfoMapper.insert(paymentInfo);
            if (insert<1){
                throw new BusinessException(ResultCodeEnum.FAIL);
            }
        }


        //然后调用aliyunApi即可
        String clientHtml = payHelper.createClient(orderInfoDTO);

        return clientHtml;
    }

    @Override
    public void savePaymentInfo(OrderInfoDTO orderInfo, String paymentTypeName) {

    }

    @Override
    public PaymentInfoDTO queryPaymentInfoByOutTradeNoAndPaymentType(String outTradeNo, String payTypeName) {
        // 根据传入的订单tradeNo和支付类型获取对应记录
        LambdaQueryWrapper<PaymentInfo> payInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        payInfoLambdaQueryWrapper.eq(PaymentInfo::getOutTradeNo,outTradeNo);
        payInfoLambdaQueryWrapper.eq(PaymentInfo::getPaymentType, payTypeName);
        PaymentInfo paymentInfo = paymentInfoMapper.selectOne(payInfoLambdaQueryWrapper);
        PaymentInfoDTO paymentInfoDTO = paymentInfoConverter.convertPaymentInfoToDTO(paymentInfo);
        return paymentInfoDTO;
    }

    @Override
    public Boolean successPay(String outTradeNo, String name, Map<String, String> paramsMap) {
        //这个地方是验签通过，通过后要处理的操作
        /*
         * 1. 修改支付表的支付状态
         * 2. 修改订单表的订单状态
         * 3. 扣减库存
         * 4. 拆单
         */
        LambdaQueryWrapper<PaymentInfo> paymentInfoWrapper=new LambdaQueryWrapper<>();
        paymentInfoWrapper.eq(PaymentInfo::getOutTradeNo,outTradeNo);
        paymentInfoWrapper.eq(PaymentInfo::getPaymentType,name);
        PaymentInfo paymentInfo = paymentInfoMapper.selectOne(paymentInfoWrapper);


        //然后修改支付表的相关内容
        Date current=new Date();
        paymentInfo.setCallbackTime(current);
        paymentInfo.setCallbackContent(JSON.toJSONString(paramsMap));
        paymentInfo.setUpdateTime(current);
        paymentInfo.setTradeNo(paramsMap.get("trade_no"));
        paymentInfo.setPaymentStatus(PaymentStatus.PAID.name());

        int updateRow = paymentInfoMapper.updateById(paymentInfo);
        if (updateRow<1){
            return false;
        }

        //调用订单接口，判断拆单等业务逻辑
        Result<Boolean> result = orderApiClient.successPay(paymentInfo.getOrderId());
        if (!ResultCodeEnum.SUCCESS.getCode().equals(result.getCode())){
            return false;
        }


        return true;
    }

    @Override
    public void updatePaymentStatus(String outTradeNo, String name, PaymentStatus paymentStatus) {

    }

    @Override
    public String callbackNotify(Map<String, String> paramsMap) throws AlipayApiException {
        //整理一下逻辑，获取相关数据，验签，判断订单id/价格等内容。
        //幂等性校验 幂等性校验要使用redis调用相关内容
        //修改payInfo表相关内容，调用wave的相关方法锁定库存。。。。

        //先验签
        boolean rasCheck = AlipaySignature.rsaCheckV1(paramsMap, csmallAlipayConfig.getAlipayPublicKey(), CsmallAlipayConfig.charset, CsmallAlipayConfig.sign_type);
        //如果验签失败
        if (!rasCheck){
            return "failure";
        }
        //获取相关值，进行判断
        String outTradeNo = paramsMap.get("out_trade_no");//我们传入的订单的tradeNo
        String notifyId = paramsMap.get("notify_id");//订单唯一性的id
        String totalAmount = paramsMap.get("total_amount");//总金额
        String tradeStatus = paramsMap.get("trade_status");//获取订单交易状态
        String appId = paramsMap.get("app_id");

        //对appid进行校验
        if (!csmallAlipayConfig.getAppId().equals(appId)){
            return "failure";
        }

        //先校验tradeNo是否存在
        PaymentInfoDTO paymentInfoDTO = this.queryPaymentInfoByOutTradeNoAndPaymentType(outTradeNo, PaymentType.ALIPAY.name());

        if (paymentInfoDTO==null){
            log.error("没找到paymentInfo");
            return "failure";
        }

        //总金额的判断和订单交易状态的判断
        BigDecimal totalAmountNum = new BigDecimal(totalAmount);
        if (!paymentInfoDTO.getTotalAmount().equals(totalAmountNum)||!"TRADE_SUCCESS".equals(tradeStatus)){
            return "failure";
        }

        //然后我们进行高级的幂等性校验
        String key= RedisConst.PAY_CALL_BACK_VERFY_PREFIX+notifyId;
        RBucket<Object> bucket = redissonClient.getBucket(key);
        boolean successSet = bucket.trySet(outTradeNo);
        if (!successSet){
            //幂等性校验失败
            return "success";
        }
        //然后进行修改相关状态
        Boolean updatePayRet = successPay(outTradeNo,PaymentType.ALIPAY.name(),paramsMap);
        if (!updatePayRet){
            //如果失败要清空相关内容
            bucket.delete();
            return "failure";
        }


        return "success";
    }

    @Override
    public PaymentInfoDTO getPaymentInfoDTOByOutTradeNo(String outTradeNo) {
        LambdaQueryWrapper<PaymentInfo> payInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        payInfoLambdaQueryWrapper.eq(PaymentInfo::getOutTradeNo,outTradeNo);
        PaymentInfo paymentInfo = paymentInfoMapper.selectOne(payInfoLambdaQueryWrapper);
        PaymentInfoDTO paymentInfoDTO = paymentInfoConverter.convertPaymentInfoToDTO(paymentInfo);
        return paymentInfoDTO;
    }

    @Override
    public String getAlipayInfo(String outTradeNo) throws AlipayApiException {
        //要写一个根据订单id获取支付状态的方法
        String tradeStatus = payHelper.getTradeStatus(outTradeNo);
        return tradeStatus;
    }

    @Override
    public void closePaymentInfo(String outTradeNo) {
        //更改对应支付状态为已关闭
        PaymentInfoDTO paymentInfoDTO = getPaymentInfoDTOByOutTradeNo(outTradeNo);
        PaymentInfo paymentInfo = new PaymentInfo();
        paymentInfo.setId(paymentInfoDTO.getId());
        paymentInfo.setPaymentStatus(PaymentStatus.CLOSED.name());
        int updateRows = paymentInfoMapper.updateById(paymentInfo);
        if (updateRows<1){
            throw new BusinessException("关闭支付失败",ResultCodeEnum.FAIL.getCode());
        }

    }

    @Override
    public void closeAlipay(String outTradeNo) throws AlipayApiException {
        //调用payHandle方法
        boolean result = payHelper.closeAlipay(outTradeNo);
        if (!result){
            throw new BusinessException("关闭支付宝订单失败",ResultCodeEnum.FAIL.getCode());
        }

    }
}
